package com.hmkj.core.service.account.impl;

import com.alibaba.fastjson.JSON;
import com.hmkj.common.baseMapper.GenericMapper;
import com.hmkj.common.utils.DateUtils;
import com.hmkj.common.utils.OrderNoUtils;
import com.hmkj.common.utils.StringUtils;
import com.hmkj.core.api.lianlian.*;
import com.hmkj.core.api.lianlian.enums.PayResultEnum;
import com.hmkj.core.constant.ConfigNID;
import com.hmkj.core.constant.LockId;
import com.hmkj.core.exception.BussinessException;
import com.hmkj.core.mapper.account.AccountCashMapper;
import com.hmkj.core.mapper.account.AccountLogMapper;
import com.hmkj.core.mapper.account.AccountMapper;
import com.hmkj.core.mapper.user.UserMapper;
import com.hmkj.core.mapper.userAuth.UserAuthMapper;
import com.hmkj.core.mapper.userBank.UserBankMapper;
import com.hmkj.core.model.account.CashModel;
import com.hmkj.core.po.account.Account;
import com.hmkj.core.po.account.AccountCash;
import com.hmkj.core.po.account.AccountLog;
import com.hmkj.core.po.user.User;
import com.hmkj.core.po.userAuth.UserAuth;
import com.hmkj.core.po.userBank.UserBank;
import com.hmkj.core.service.account.AccountCashService;
import com.hmkj.core.service.cache.LockService;
import com.hmkj.core.service.cache.RedisService;
import com.lianlianpay.security.utils.LianLianPaySecurity;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * 提现记录业务相关Service接口实现<br>
 *
 * @author eproo
 * @ClassName: AccountCashServiceImpl
 * @date 2018-07-05 09:50:54
 */
@Slf4j
@Service
@Scope("prototype")
@Transactional(rollbackFor = Exception.class)
public class AccountCashServiceImpl implements AccountCashService {

    @Resource
    private AccountCashMapper accountCashMapper;
    @Resource
    private UserMapper userMapper;
    @Resource
    private UserAuthMapper userAuthMapper;
    @Resource
    private AccountLogMapper accountLogMapper;
    @Resource
    private AccountMapper accountMapper;
    @Resource
    private UserBankMapper userBankMapper;
    @Resource
    private LockService lockService;
    @Resource
    private RedisService redisService;

    @Override
    public GenericMapper<AccountCash, Integer> _getMapper() {
        return accountCashMapper;
    }

    @Override
    public void addCashMoney(AccountCash accountCash) throws Exception {

        if (accountCash == null) {
            throw new BussinessException("参数错误");
        }
        User user = userMapper.selectByPrimaryKey(accountCash.getUserId());
        if (user == null) {
            throw new BussinessException("客户不存在");
        }
        if (accountCash.getAmount().compareTo(BigDecimal.ZERO) != 1) {
            throw new BussinessException("提现金额必须大于0");
        }
        if (StringUtils.isBlank(accountCash.getOrderNo())) {
            throw new BussinessException("流水号不能为空");
        }
        if (accountCash.getOrderNo().length() > 50) {
            throw new BussinessException("流水号长度不能超过50位");
        }
        if (StringUtils.isBlank(accountCash.getRemark())) {
            throw new BussinessException("备注不能为空");
        }
        if (accountCash.getRemark().length() > 200) {
            throw new BussinessException("备注长度不能超过200位");
        }
        Account account = accountMapper.selectByUID(accountCash.getUserId());

        accountCashMapper.insert(new AccountCash(t -> {
            t.setUserId(accountCash.getUserId());
            t.setAmount(accountCash.getAmount());
            t.setStatus(AccountCash.STATUS.T4.code);
            t.setOrderNo(accountCash.getOrderNo());
            t.setBankName(accountCash.getBankName());
            t.setBankNo(accountCash.getBankNo());
            t.setBranch(accountCash.getBranch());
            t.setRemark(accountCash.getRemark());
        }));

        //资金记录
        AccountLog accountLog = AccountLog.buildLog(accountCash.getUserId(), 0, accountCash.getAmount(), AccountLog.TYPE.T2, account, "手动提现：" + accountCash.getAmount() + "元");
        if (accountLogMapper.insert(accountLog) != 1) {
            throw new BussinessException("手动提现资金记录失败");
        }
        //更新账户资金
        if (accountMapper.updateAccount(accountCash.getAmount().negate(), accountCash.getAmount().negate(), BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, accountCash.getUserId()) != 1) {
            throw new BussinessException("手动提现失败");
        }
    }

    @Override
    public void createCash(String bankCardId, BigDecimal money, User user) {
        if (lockService.lock(LockId.CASH + user.getId())) {
            throw new BussinessException("您提交的提现请求正在处理，请勿重复提交");
        }
        try {
            UserAuth userAuth = userAuthMapper.selectByUID(user.getId());
            UserBank bank = userBankMapper.selectByPrimaryKey(Integer.valueOf(bankCardId));
            Account account = accountMapper.selectByUID(user.getId());
            //冻结用户资金，资金记录
            AccountLog accountLog = AccountLog.buildLog(user.getId(), 0, money, AccountLog.TYPE.T8, account, "提现申请，冻结金额" + money + "。");
            if (accountLogMapper.insert(accountLog) != 1) {
                throw new BussinessException("提现申请资金记录失败");
            }
            //更新账户资金
            if (accountMapper.updateAccount(BigDecimal.ZERO, money.negate(), BigDecimal.ZERO, money, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, user.getId()) != 1) {
                throw new BussinessException("提现更新账户信息失败");
            }

            String orderNo = OrderNoUtils.getLLRechargeOrderNo();
//            BigDecimal cashFeeRatio = redisService.get(ConfigNID.CASH_FEE_RATIO, BigDecimal.class);
//            if (cashFeeRatio == null){
//                cashFeeRatio =BigDecimal.ZERO;
//            }
//            BigDecimal cashFeeMin = redisService.get(ConfigNID.CASH_FEE_MIN, BigDecimal.class);
//            if (cashFeeMin == null) {
//                cashFeeMin = new BigDecimal(0);// 默认单笔提现最低手续费为0
//            }
//            BigDecimal cashFee = cashFeeRatio.multiply(money).multiply(BigDecimal.valueOf(0.01));
//            if(cashFeeMin.compareTo(cashFee)>0){
//                cashFee = cashFeeMin;
//            }
            AccountCash model = new AccountCash();
            model.setUserId(user.getId());
            model.setRealName(userAuth.getRealname());
            model.setIdCard(userAuth.getIdcard());
            model.setBankNo(bank.getBankNo());
            model.setBranch(bank.getBranch());
            model.setBankName(bank.getBankName());
            model.setBankCode(bank.getBankCode());
            model.setOrderNo(orderNo);
            model.setAmount(money);
            model.setType(AccountCash.TYPE.T1.code);
            model.setFee(BigDecimal.ZERO);
            model.setStatus(AccountCash.STATUS.T2.code);
            model.setNeedAudit(AccountCash.NEEDAUDIT.T1.code);
            if (accountCashMapper.insert(model) != 1) throw new BussinessException("保存提现申请失败");
        } finally {
            // 释放锁
            lockService.unlock(LockId.CASH + user.getId());

        }
    }

    @Override
    public void doAuditLianLian(CashModel model) throws Exception {
        // 提现参数校验
        AccountCash cash = accountCashMapper.selectByPrimaryKey(model.getId());
        if (null == cash) {
            throw new BussinessException("提现记录不存在");
        }
        if (cash.getStatus() != AccountCash.STATUS.T2.code) {
            throw new BussinessException("提现申请状态已更改请刷新后重试");
        }
        //更新提现订单信息
        List<UserBank> banks = userBankMapper.select(new UserBank(ub -> {
            ub.setBankNo(cash.getBankNo());
        }));
        if (CollectionUtils.isEmpty(banks)) {
            throw new BussinessException("银行卡不存在");
        }
        //根据审核状态通过，则调第三方提现接口，不通过，解冻提现金额
        if (Integer.valueOf(model.getAuditStatus()) == CashModel.AUDITSTATUS.T1.code) {

            // 调用第三方代付接口
            DaiFuResp daiFuResp = sendDaiFuNotifyByLianLian(cash);
            if (StringUtils.isNotBlank(daiFuResp.getStatus()) && daiFuResp.getStatus().equalsIgnoreCase(EPayConsts.RES_FLAG_SUCCESS)) {
                //修改订单状态
                cash.setStatus(AccountCash.STATUS.T3.code);
                accountCashMapper.updateByPrimaryKeySelective(cash);
            } else if (StringUtils.isNotBlank(daiFuResp.getStatus()) && daiFuResp.getStatus().equalsIgnoreCase(EPayConsts.RES_FLAG_UNKNOWN)) {
                cash.setStatus(AccountCash.STATUS.T3.code);
                accountCashMapper.updateByPrimaryKeySelective(cash);
//                result = StrKit.isBlank(daiFuResp.getRemark()) ? "请求结果未知" : daiFuResp.getRemark();
            } else {
//                this.doCashVerify(order.getId(), "failed", "3");
                throw new BussinessException(StringUtils.isBlank(daiFuResp.getRemark()) ? "第三方请求异常" : daiFuResp.getRemark());
            }
        } else {
            Account account = accountMapper.selectByUID(cash.getUserId());
            //解冻用户资金账户
            //资金记录
            AccountLog accountLog = AccountLog.buildLog(account.getUserId(), 0, cash.getAmount(), AccountLog.TYPE.T9, account, "提现取消，解冻金额：" + cash.getAmount() + "元");
            if (accountLogMapper.insert(accountLog) != 1) {
                throw new BussinessException("提现解冻资金记录失败");
            }

            //更新账户资金
            if (accountMapper.updateAccount(BigDecimal.ZERO, cash.getAmount(), BigDecimal.ZERO, cash.getAmount().negate(), BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, cash.getUserId()) != 1) {
                throw new BussinessException("提现解冻资金失败");
            }
            cash.setStatus(AccountCash.STATUS.T6.code);
            cash.setRemark(model.getRemark());
            accountCashMapper.updateByPrimaryKeySelective(cash);
        }
    }

    @Override
    public void doAuditFail(CashModel model) throws Exception {
        // 提现参数校验
        AccountCash cash = accountCashMapper.selectByPrimaryKey(model.getId());
        if (null == cash) {
            throw new BussinessException("提现记录不存在");
        }
        if (cash.getStatus() != AccountCash.STATUS.T2.code) {
            throw new BussinessException("提现申请状态已更改请刷新后重试");
        }
        Account account = accountMapper.selectByUID(cash.getUserId());
        if (account == null) {
            throw new BussinessException("查询用户账户失败");
        }

        //解冻用户资金，资金记录
        AccountLog accountLog = AccountLog.buildLog(cash.getUserId(), 0, model.getAmount(), AccountLog.TYPE.T9, account, "提现审核不通过，解冻金额" + model.getAmount() + "。");
        if (accountLogMapper.insert(accountLog) != 1) {
            throw new BussinessException("提现解冻资金记录失败");
        }
        //更新账户资金
        if (accountMapper.updateAccount(BigDecimal.ZERO, model.getAmount(), BigDecimal.ZERO, model.getAmount().negate(), BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, cash.getUserId()) != 1) {
            throw new BussinessException("提现解冻账户信息失败");
        }


        //处理提现订单
        int i = accountCashMapper.updateByPrimaryKeySelective(new AccountCash(t -> {
            t.setId(cash.getId());
            t.setUpdateTime(new Date());
            t.setStatus(AccountCash.STATUS.T5.code);
            t.setRemark(model.getRemark());
        }));
        if (i != 1) {
            throw new BussinessException("更新提现东单失败");
        }
    }

    @Override
    public void handleOrderByLianLian(PayDataBean payDataBean) throws Exception {
        AccountCash cash = accountCashMapper.selectOne(new AccountCash(c -> {
            c.setOrderNo(payDataBean.getNo_order());
        }));
        if (null == cash) {
            throw new BussinessException("订单为空");
        }
        Account account = accountMapper.selectByUID(cash.getUserId());
        if (cash.getStatus() == AccountCash.STATUS.T3.code) {
            if (cash.getAmount().doubleValue() == new BigDecimal(payDataBean.getMoney_order()).doubleValue() && Objects.equals(PayResultEnum.SUCCESS.getCode(), payDataBean.getResult_pay())) {
                //资金记录
                AccountLog accountLog = AccountLog.buildLog(account.getUserId(), 0, cash.getAmount(), AccountLog.TYPE.T28, account, "提现扣除，扣除金额：" + cash.getAmount() + "元");
                if (accountLogMapper.insert(accountLog) != 1) {
                    throw new BussinessException("提现扣除资金记录失败");
                }
                //更新账户资金
                if (accountMapper.updateAccount(cash.getAmount().negate(), BigDecimal.ZERO, BigDecimal.ZERO, cash.getAmount().negate(), BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, cash.getUserId()) != 1) {
                    throw new BussinessException("提现扣除资金失败");
                }

                //处理提现订单
                cash.setActualArrival(new BigDecimal(payDataBean.getMoney_order()));
                cash.setUpdateTime(new Date());
                cash.setOutOrderNo(payDataBean.getOid_paybill());
                cash.setOutMsg(JSON.toJSONString(payDataBean));
                cash.setStatus(AccountCash.STATUS.T4.code);
                accountCashMapper.updateByPrimaryKeySelective(cash);
            } else {
                // 退款这种情况是极小概率情况下才会发生的，个别银行处理机制是先扣款后再打款给用户时，
                // 才检验卡号姓名信息的有效性，当卡号姓名信息有误发生退款，实际上钱没打款到商户。
                // 这种情况商户代码上也可不做考虑，如发生用户投诉未收到钱，可直接联系连连客服，连连会跟银行核对
                // 退款情况，异步通知会通知两次，先通知成功，后通知退款（极小概率情况下才会发生的）

                //解冻用户资金账户
                //资金记录
                AccountLog accountLog = AccountLog.buildLog(account.getUserId(), 0, cash.getAmount(), AccountLog.TYPE.T9, account, "提现解冻，解冻金额：" + cash.getAmount() + "元");
                if (accountLogMapper.insert(accountLog) != 1) {
                    throw new BussinessException("提现解冻资金记录失败");
                }

                //更新账户资金
                if (accountMapper.updateAccount(BigDecimal.ZERO, cash.getAmount(), BigDecimal.ZERO, cash.getAmount().negate(), BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, cash.getUserId()) != 1) {
                    throw new BussinessException("提现解冻资金失败");
                }
                //处理提现订单
                cash.setUpdateTime(new Date());
                cash.setStatus(AccountCash.STATUS.T5.code);
                cash.setOutOrderNo(payDataBean.getOid_paybill());
                cash.setOutMsg(JSON.toJSONString(payDataBean));
                accountCashMapper.updateByPrimaryKeySelective(cash);
            }
        }
    }

    private DaiFuResp sendDaiFuNotifyByLianLian(AccountCash cash) throws Exception {
//        SysBank sysBank = SysBankService.SERVICE.getByExtCode(bankCard.getBankCode());
//        if (sysBank == null) {
//            throw new BussinessException("系统银行卡不存在，请联系管理员");
//        }
//        String bankAgentId = sysBank.getLianHangNo();
//
//        if (StringUtils.isBlank(bankAgentId)) {
//            throw new BussinessException("收款人账户银行联行号不存在，请联系管理员");
//        }
        PaymentRequestBean paymentRequestBean = new PaymentRequestBean();
        String orderNo = cash.getOrderNo();
        paymentRequestBean.setNo_order(orderNo);
        paymentRequestBean.setDt_order(DateUtils.dateStr3(new Date()));
        paymentRequestBean.setMoney_order(String.valueOf(cash.getAmount()));
        paymentRequestBean.setCard_no(cash.getBankNo());
        paymentRequestBean.setAcct_name(cash.getRealName());
        paymentRequestBean.setInfo_order("代付");
        paymentRequestBean.setFlag_card("0");
        paymentRequestBean.setMemo("代付");
        // 填写商户自己的接收付款结果回调异步通知
        String webUrl = redisService.get(ConfigNID.WEB_SERVER_URL);
        paymentRequestBean.setNotify_url(webUrl + PartnerConfig.DF_NOTIFY_URL);
        paymentRequestBean.setOid_partner(PartnerConfig.OID_PARTNER);
        paymentRequestBean.setApi_version(PartnerConfig.VERSION);
        paymentRequestBean.setSign_type(PartnerConfig.SIGN_TYPE);
        // 用商户自己的私钥加签
        paymentRequestBean.setSign(SignUtil.genRSASign(JSON.parseObject(JSON.toJSONString(paymentRequestBean))));
        String jsonStr = JSON.toJSONString(paymentRequestBean);
        log.info("实时付款请求报文：" + jsonStr);
        // 用银通公钥对请求参数json字符串加密
        // 报Illegal key
        // size异常时，可参考这个网页解决问题http://www.wxdl.cn/java/security-invalidkey-exception.html
        String encryptStr = LianLianPaySecurity.encrypt(jsonStr, PartnerConfig.YT_PUB_KEY);
        if (StringUtils.isBlank(encryptStr)) {
            // 加密异常
            log.error("加密异常:");
            throw new BussinessException("加密异常");
        }
        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        json.put("oid_partner", PartnerConfig.OID_PARTNER);
        json.put("pay_load", encryptStr);
        String response = HttpUtil.post(PartnerConfig.DF_URL, json, "UTF-8");
        System.out.println("响应：" + response);
        DaiFuResp daiFuResp = new DaiFuResp();
        if (StringUtils.isBlank(response)) {
            throw new BussinessException("请求数据为空");
        }
        PaymentResponseBean paymentResponseBean = com.alibaba.fastjson.JSONObject.parseObject(response, PaymentResponseBean.class);
        String retCode = paymentResponseBean.getRet_code();
        if ("0000".equals(retCode)) {
            daiFuResp.setStatus(EPayConsts.RES_FLAG_SUCCESS);
        } else if ("4002".equals(paymentResponseBean.getRet_code()) || "4004".equals(paymentResponseBean.getRet_code())) {
            daiFuResp.setStatus(EPayConsts.RES_FLAG_FALSE);
        } else if (RetCodeEnum.isNeedQuery(paymentResponseBean.getRet_code())) {
            // 出现1002，2005，4006，4007，4009，9999这6个返回码时（或者对除了0000之后的code都查询一遍查询接口）调用付款结果查询接口，明确订单状态，不能私自设置订单为失败状态，以免造成这笔订单在连连付款成功了，而商户设置为失败
            // 第一次测试对接时，返回{"ret_code":"4007","ret_msg":"敏感信息解密异常"},可能原因报文加密用的公钥改动了,demo中的公钥是连连公钥，商户生成的公钥用于上传连连商户站用于连连验签，生成的私钥用于加签
            daiFuResp.setStatus(EPayConsts.RES_FLAG_UNKNOWN);
        } else {
            daiFuResp.setStatus(EPayConsts.RES_FLAG_FALSE);
        }
        daiFuResp.setRemark(paymentResponseBean.getRet_msg());
        return daiFuResp;
    }

}
